#include "OP_Translate.h"
#include "../Core/Vector3D.h"

using namespace std;
using namespace CICore;
using namespace CIGeom;
using namespace CIView;

///<************************************************************************************************************>
///<************************************* region CONSTANTS DEFINITION ******************************************>
///<************************************************************************************************************>

/***************************************************************************************************************
************************************** endregion CONSTANTS DEFINITION ******************************************
****************************************************************************************************************/







/// <summary> 
/// Constructor 
/// </summary>
/// <param name="v"> A reference to the vector that contains the four 3D views pointers </param>
CIOps::OP_Translate::OP_Translate (vector<CIView::View*>& v)
: Operation (v) {}

/// <summary> 
/// Destructor 
/// </summary>
CIOps::OP_Translate::~OP_Translate() {}










///<************************************************************************************************************>
///<**************************************** region Utility Methods ********************************************>
///<************************************************************************************************************>

/// <summary> 
/// Method called to execute directly the operation 
/// </summary>
/// <returns> The operation status </returns>
int CIOps::OP_Translate::Exec() {
    Vector3D& TranslateParams = OperationManager::X_Y_Z_Params;

    if (TranslateParams.x == 0 && TranslateParams.y == 0 && TranslateParams.z == 0) {
        status = ZOMBIE;
        return status;
    }

    translationMatrix.SetTranslation (TranslateParams.x, 
                                      TranslateParams.y, 
                                      TranslateParams.z);       //The matrix to apply 

    scene.ApplyMatrix (translationMatrix);                      //Applies the transformation to the selected vertices

    CIView::View::Feedback_Translation.ApplyMatrix (translationMatrix);   //Applies the matrix to the feedback position of the four 3D views

    inverseMatrix = translationMatrix.GetInverse();         //Computes the new inverse matrix (used to undo the transformation)

    UpdateViews();                                          //Updates the 3D views

    status = COMPLETED;
    
    return status;        
}

/// <summary> 
/// Method called to undo the operation 
/// </summary>
void CIOps::OP_Translate::Undo() {
    status = UNDO;

    scene.ApplyMatrix (inverseMatrix);  //Applies the last inverse matrix

    RestoreFeedbackPosition();

    UpdateViews();                      //Updates the 3D views
}

/// <summary> 
/// Method called to redo the operation 
/// </summary>
void CIOps::OP_Translate::Redo() {
    status = COMPLETED;

    scene.ApplyMatrix (translationMatrix);  //Applies the last translation matrix

    RestoreFeedbackPosition();

    UpdateViews();                          //Updates the 3D views
}

/// <summary> 
/// Mouse Pressed event handler, called whenever the operation 
/// is in execution and a 3D view intercepts a mouse press event
/// </summary>
/// <param name="view"> The view that has thrown the event </param>
/// <param name="e"> Mouse Pressed event </param>
void CIOps::OP_Translate::MousePressEvent (CIView::View* view, QMouseEvent* e) {
    float x = e->x();
    float y = e->y();

    selected = view->SelectFeedbackTranslationAxes (x, y);      //Selects the axes according to the mouse click coordinates

    view->WindowToWorldCoordinates (&x, &y);                    //Unproject the mouse cursor coordinates

    //Computing the mouse click coordinates (in world coordinates)
    switch (view->GetCameraType()) {
        case Camera::TOP:
            click.x = x;
            click.z = y;
            break;
        
        case Camera::FRONT:
            click.x = x;
            click.y = y;
            break;
        
        case Camera::RIGHT:   
            click.z = x;         
            click.y = y;
            break;
        
        case Camera::USER:          
            click.x = x;
            click.y = y;

            break;
    }

    CIView::View::Feedback_Translation.LockInitialPosition();

    UpdateViews();                                              //Updates the 3D views
}

/// <summary> 
/// Mouse Released event handler, called whenever the operation 
/// is in execution and a 3D view intercepts a mouse release event
/// </summary>
/// <param name="view"> The view that has thrown the event </param>
/// <param name="e"> Mouse Released event </param>
void CIOps::OP_Translate::MouseReleaseEvent (CIView::View* view, QMouseEvent* e) {
    if (drag.Length() > 0)
        status = COMPLETED;
    else
        status = ZOMBIE;

    CIView::View::Feedback_Translation.DeselectAll();
    UpdateViews();
}

/// <summary> 
/// Mouse Moved event handler, called whenever the operation 
/// is in execution and a 3D view intercepts a mouse move event
/// </summary>
/// <param name="view"> The view that has thrown the event </param>
/// <param name="e"> Mouse Moved event </param>
void CIOps::OP_Translate::MouseMoveEvent (CIView::View* view, QMouseEvent* e) {
    float x = e->x();
    float y = e->y();

    view->WindowToWorldCoordinates (&x, &y);            //Unproject the mouse cursor coordinates    

    //Computing the mouse drag value
    switch (view->GetCameraType()) {
        case Camera::TOP:
            drag.x = x - click.x;
            drag.z = y - click.z;
            break;
        
        case Camera::FRONT:
            drag.x = x - click.x;
            drag.y = click.y - y;
            break;
        
        case Camera::RIGHT:       
            drag.z = click.z - x;     
            drag.y = click.y - y;
            break;

        case Camera::USER:          //TODO -> Consider the camera orientation on the z axis too!!!
            Orientation& cameraOrient = view->GetCameraOrientation();       //Gets the current camera orientation

            //Computing the drag on the x axis (depends on the x, y and z camera angles)
            if ((cameraOrient.y >= 300 || cameraOrient.y <= 60))
                drag.x = x - click.x;
            else if ((cameraOrient.y >=120 && cameraOrient.y <= 240))
                drag.x = click.x - x;
            else if ((cameraOrient.y > 60 && cameraOrient.y < 120)) {
                if (cameraOrient.x > 180 && cameraOrient.x < 360)
                    drag.x = y - click.y;
                else
                    drag.x = click.y - y;
            }
            else if ((cameraOrient.y < 300 && cameraOrient.y > 240)) {
                if (cameraOrient.x > 180 && cameraOrient.x < 360)
                    drag.x = click.y - y;
                else
                    drag.x = y - click.y;                
            }

            //Computing the drag on the y axis (depends on the x and z camera angles)
            if (cameraOrient.x >= 90 && cameraOrient.x <= 270)
                drag.y = y - click.y;
            else
                drag.y = click.y - y;

            //Computing the drag on the z-axis (depends on the x, y and z camera angles)
            if ((cameraOrient.y >= 300 || cameraOrient.y <= 60)) {
                if (cameraOrient.x > 180 && cameraOrient.x < 360)
                    drag.z = click.y - y;
                else
                    drag.z = y - click.y;
            }
            else if ((cameraOrient.y >=120 && cameraOrient.y <= 240)) {
                if (cameraOrient.x > 180 && cameraOrient.x < 360)
                    drag.z = y - click.y;
                else
                    drag.z = click.y - y;
            }
            else if ((cameraOrient.y > 60 && cameraOrient.y < 120))
                drag.z = x - click.x;
            else if ((cameraOrient.y < 300 && cameraOrient.y > 240))
                drag.z = click.x - x;
            break;
    }    

    //Setting up the translation matrix according to the selected axes
    switch (selected) {
        case FeedbackTranslation::X_AXIS:
            translationMatrix.SetTranslation (drag.x, 0, 0);
            break;

        case FeedbackTranslation::Y_AXIS:
            translationMatrix.SetTranslation (0, drag.y, 0);
            break;
            
        case FeedbackTranslation::Z_AXIS:
            translationMatrix.SetTranslation (0, 0, drag.z);
            break;

        case FeedbackTranslation::ALL_AXES:
            translationMatrix.SetTranslation (drag.x, drag.y, drag.z);
            break;
    }

    Matrix toApply;
    toApply = inverseMatrix*translationMatrix;       //The matrix to apply for the transformation has to consider the previous transformation

    scene.ApplyMatrix (toApply);                            //Applies the transformation to the selected vertices

    CIView::View::Feedback_Translation.ApplyMatrix (toApply);       //Applies the matrix to the feedback position of the four 3D views

    inverseMatrix = translationMatrix.GetInverse();         //Computes the new inverse matrix (used to undo the transformation)

    UpdateViews();                                          //Updates the 3D views
}

/// <summary> 
/// Wheel event handler, called whenever the operation 
/// is in execution and a 3D view intercepts a mouse wheel event
/// </summary>
/// <param name="view"> The view that has thrown the event </param>
/// <param name="e"> Wheel event </param>
void CIOps::OP_Translate::WheelEvent (CIView::View* view, QWheelEvent* e) {
}
/***************************************************************************************************************
***************************************** endregion Utility Methods ********************************************
****************************************************************************************************************/
